package com.example.file.service;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.ObjectId;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.CannedAccessControlList;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;

import com.example.core.constants.CacheConstants;
import com.example.core.utils.StringUtils;
import com.example.file.config.OSSProperties;
import com.example.file.domain.MimeType;
import com.example.file.domain.OSSEntity;
import com.example.file.domain.OSSResult;
import com.example.file.domain.SysFile;
import com.example.redis.service.RedisService;
import com.example.scurity.exception.ServiceException;
import com.example.scurity.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class OSSService {

    @Autowired
    private OSSProperties prop;

    @Autowired
    private OSSClient ossClient;

    @Autowired
    private RedisService redisService;

    @Value("${file.max-time:3}")
    private int maxTime;

    @Value("${file.test:false}")
    private boolean test;

    @Value("${file.path-prefix:}")
    private String pathPrefix;

    public SysFile uploadFile(MultipartFile file) throws Exception {
        if (!test) {
            checkUploadCount();
        }
        MimeType mimeType;
        OSSEntity entity = null;
        InputStream inputStream = null;
        try {
            try {
                String extName = "";
                if (file.getOriginalFilename() != null) {
                    extName = file.getOriginalFilename().toUpperCase();
                } else {
                    extName = "a.png";
                }

                extName = extName.substring(extName.lastIndexOf(".") + 1);
                mimeType = MimeType.valueOf(extName);
            } catch (Exception e) {
                log.error("OSS upload file error: {}", ExceptionUtil.stacktraceToOneLineString(e, 500));
                throw new ServiceException("文件格式不支持");
            }
            inputStream = file.getInputStream();

            entity = new OSSEntity();
            entity.setContentType(mimeType);
            entity.setContentLength(file.getSize());
            entity.setInputStream(inputStream);
            OSSResult ossResult = upload(entity);
            SysFile sysFile = new SysFile();
            sysFile.setKey(ossResult.getKey());
            sysFile.setUrl(ossResult.getUrl());
            sysFile.setName(FileUtil.getName(ossResult.getUrl()));
            return sysFile;
        } catch (Exception e) {
            throw new ServiceException("OSS上传文件失败");
        } finally {
            if (entity != null && entity.getInputStream() != null) {
                entity.getInputStream().close();
            }
        }
    }

    private void checkUploadCount() {
        Long userId = SecurityUtils.getUserId();
        Long times = redisService.getCacheMapValue(CacheConstants.USER_UPLOAD_TIMES_KEY, String.valueOf(userId), Long.class);
        if (times != null && times >= maxTime) {
            throw new ServiceException("当天上传图片数量超过上限");
        }
        redisService.incrementHashValue(CacheConstants.USER_UPLOAD_TIMES_KEY, String.valueOf(userId), 1);
        long seconds = ChronoUnit.SECONDS.between(LocalDateTime.now(),
                LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0));
        redisService.expire(CacheConstants.USER_UPLOAD_TIMES_KEY, seconds, TimeUnit.SECONDS);
    }

    private OSSResult upload(OSSEntity ossEntity) {
        // key pattern: file/id.xxx, cannot start with /
        String key = pathPrefix + ObjectId.next() + "." + ossEntity.getContentType().getExtend();

        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentType(ossEntity.getContentType().getMimeType());
        objectMetadata.setContentEncoding("UTF-8");
        objectMetadata.setContentLength(ossEntity.getContentLength());
        objectMetadata.setContentDisposition("inline");
        // set public read
        objectMetadata.setObjectAcl(CannedAccessControlList.PublicRead);

        PutObjectRequest request = new PutObjectRequest(prop.getBucketName(), key, ossEntity.getInputStream(), objectMetadata);
        PutObjectResult putObjectResult = null;
        try {
            putObjectResult = ossClient.putObject(request);
        } catch (Exception e) {
            log.error("OSS put object error: {}", ExceptionUtil.stacktraceToOneLineString(e, 500));
            throw new ServiceException("OSS put object error");
        }


        OSSResult ossResult = new OSSResult(key);
        if (putObjectResult == null || StringUtils.isBlank(putObjectResult.getRequestId())) {
            ossResult.setSuccess(false);
            ossResult.setUrl(null);
        } else {
            ossResult.setSuccess(true);
            // 下载地址：域名+key
            ossResult.setUrl(prop.getDownloadUrl(key));
        }
        return ossResult;
    }

}
